// 定义状态常量。使编辑器可以自动提醒
const PENDING   = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED  = 'rejected'

class MyPromise {
    PromiseState = PENDING   // 对象状态
    PromiseResult = null     // 对象结果

    tmpFulfilleds = []  // 暂存成功回调
    tmpRejecteds  = []  // 暂存失败回调

	// 构造方法. new 的时候自动执行
    constructor (executor) {
        try {
          executor(this.resolve, this.reject)
        } catch (e) {
          this.reject(e)  // 异常时, 设置为失败状态
        }
    }

    // 设置 成功 状态+结果   为 this 使用箭头函数
    resolve = value => {
        // 如果状态不为待定,则函数直接返回. 确保状态不可逆
        if (this.PromiseState !== PENDING) return
        this.PromiseState = FULFILLED
        this.PromiseResult = value
		
        // 设置成功状态时, 循环执行暂存的成功回调函数
        while (this.tmpFulfilleds.length) {
            this.tmpFulfilleds.shift()()
        }
    }
    // 设置 失败 状态+结果   为 this 使用箭头函数
    reject = reason => {
        // 如果状态不为待定,则函数直接返回. 确保状态不可逆
        if (this.PromiseState !== PENDING) return
        this.PromiseState = REJECTED
        this.PromiseResult = reason
        
        // 设置失败状态时, 循环执行暂存的失败回调函数
        while (this.tmpRejecteds.length) {
            this.tmpRejecteds.shift()()
        }
    }
	
    // 状态和结果确定-之后. 设置相应回调函数. 
    then (onFulfilled, onRejected) {
        // 参数不是函数时, 给默认值
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
        onRejected  = typeof onRejected  === 'function' ? onRejected : reason => {throw reason}
        const thenPromise =  new MyPromise((resolve, reject) => {
            let resolvePromise = cb => {
                queueMicrotask(() => {
                    try {
                        let x = cb(this.PromiseResult)
                        if (x === thenPromise) {
                            return reject(new Error('不能返回自身~'))
                        }
                        if (x instanceof MyPromise) {
                            // x.then(val=>resolve(val), reject)
                            x.then(val => {
                              resolvePromise(()=>val)
                            }, reason => {
                              resolvePromise(()=>reason)
                            })
                        } else {
                            resolve(x)
                        }
                    } catch (e) {
                        reject(e)
                    }
                })
            }
            if (this.PromiseState === FULFILLED) {
                resolvePromise(onFulfilled)
            } else if (this.PromiseState === REJECTED) {
                resolvePromise(onRejected)
            } else if (this.PromiseState === PENDING) {
                this.tmpFulfilleds.push( resolvePromise.bind(this, onFulfilled) )
                this.tmpRejecteds.push( resolvePromise.bind(this, onRejected) )
            }
        })
        return thenPromise
    }

    static all (arr) {
        const result = []
        let count = 0
        return new Promise((resolve, reject) => {
            let addData = (i, val) => {
                result[i] = val
                count++
                if (count === arr.length) {
                    resolve(result)
                }
            }
            arr.forEach((item, index) => {
                if (item instanceof MyPromise) {
                    item.then(value=>addData(index, value), reject)
                } else {
                    addData(index, item)
                }
            })
        })
    }

    static race (arr) {
        return new MyPromise((resolve, reject) => {
            arr.forEach(item => {
                if (item instanceof MyPromise) {
                    item.then(resolve, reject)
                } else {
                    queueMicrotask(() => {
                        resolve(item)
                    })
                }
            })
        })
    }

    static resolve (param) {
        if (param instanceof MyPromise) return param
        return new MyPromise(resolve => resolve(param))
    }

    static reject (param) {
        return new MyPromise((resolve, reject) => reject(param))
    }

    finally (callback) {
        let x = typeof callback === 'function' ? callback() : callback
        return MyPromise.resolve(x).then(()=>this, reason=>{throw reason})
    }

    catch (onRejected) {
        return this.then(null, onRejected)
    }
}